{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "11934bca",
   "metadata": {},
   "source": [
    "## 维度变换操作\n",
    "- View / reshape 改变形状\n",
    "- Squeeze / unsqueeze  挤压/增加维度\n",
    "- Transpose / t / permute 交换/转置/重排序\n",
    "- Expand / repeat  维度扩展\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "6fd8ab21",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f5a99d7",
   "metadata": {},
   "source": [
    "### View / reshape 改变形状"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4f55d8a5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 1, 28, 28])\n",
      "tensor([[0.6544, 0.3580, 0.1400,  ..., 0.8024, 0.0342, 0.2876],\n",
      "        [0.3359, 0.1792, 0.7179,  ..., 0.2769, 0.0924, 0.6806],\n",
      "        [0.6554, 0.1104, 0.2207,  ..., 0.6217, 0.2140, 0.4040],\n",
      "        [0.8468, 0.5219, 0.6235,  ..., 0.4917, 0.1469, 0.3527]])\n",
      "torch.Size([4, 784])\n",
      "torch.Size([112, 28])\n",
      "torch.Size([4, 28, 28])\n",
      "tensor([[[[0.6544],\n",
      "          [0.3580],\n",
      "          [0.1400],\n",
      "          ...,\n",
      "          [0.4785],\n",
      "          [0.5513],\n",
      "          [0.8632]],\n",
      "\n",
      "         [[0.0037],\n",
      "          [0.8917],\n",
      "          [0.4363],\n",
      "          ...,\n",
      "          [0.4161],\n",
      "          [0.4266],\n",
      "          [0.5922]],\n",
      "\n",
      "         [[0.2431],\n",
      "          [0.0944],\n",
      "          [0.2461],\n",
      "          ...,\n",
      "          [0.9726],\n",
      "          [0.1541],\n",
      "          [0.3869]],\n",
      "\n",
      "         ...,\n",
      "\n",
      "         [[0.5716],\n",
      "          [0.2602],\n",
      "          [0.6733],\n",
      "          ...,\n",
      "          [0.7753],\n",
      "          [0.9051],\n",
      "          [0.9947]],\n",
      "\n",
      "         [[0.4941],\n",
      "          [0.0616],\n",
      "          [0.4745],\n",
      "          ...,\n",
      "          [0.1104],\n",
      "          [0.6531],\n",
      "          [0.1984]],\n",
      "\n",
      "         [[0.6593],\n",
      "          [0.4214],\n",
      "          [0.8910],\n",
      "          ...,\n",
      "          [0.8024],\n",
      "          [0.0342],\n",
      "          [0.2876]]],\n",
      "\n",
      "\n",
      "        [[[0.3359],\n",
      "          [0.1792],\n",
      "          [0.7179],\n",
      "          ...,\n",
      "          [0.3978],\n",
      "          [0.5515],\n",
      "          [0.8929]],\n",
      "\n",
      "         [[0.5975],\n",
      "          [0.1197],\n",
      "          [0.8530],\n",
      "          ...,\n",
      "          [0.5554],\n",
      "          [0.6778],\n",
      "          [0.2346]],\n",
      "\n",
      "         [[0.8694],\n",
      "          [0.6462],\n",
      "          [0.2741],\n",
      "          ...,\n",
      "          [0.2353],\n",
      "          [0.1072],\n",
      "          [0.3699]],\n",
      "\n",
      "         ...,\n",
      "\n",
      "         [[0.6897],\n",
      "          [0.5827],\n",
      "          [0.0391],\n",
      "          ...,\n",
      "          [0.1034],\n",
      "          [0.8615],\n",
      "          [0.5261]],\n",
      "\n",
      "         [[0.2165],\n",
      "          [0.2233],\n",
      "          [0.3458],\n",
      "          ...,\n",
      "          [0.9036],\n",
      "          [0.4866],\n",
      "          [0.2859]],\n",
      "\n",
      "         [[0.8012],\n",
      "          [0.6022],\n",
      "          [0.3708],\n",
      "          ...,\n",
      "          [0.2769],\n",
      "          [0.0924],\n",
      "          [0.6806]]],\n",
      "\n",
      "\n",
      "        [[[0.6554],\n",
      "          [0.1104],\n",
      "          [0.2207],\n",
      "          ...,\n",
      "          [0.0158],\n",
      "          [0.1860],\n",
      "          [0.3155]],\n",
      "\n",
      "         [[0.4973],\n",
      "          [0.1678],\n",
      "          [0.3468],\n",
      "          ...,\n",
      "          [0.8190],\n",
      "          [0.8410],\n",
      "          [0.3526]],\n",
      "\n",
      "         [[0.4234],\n",
      "          [0.0121],\n",
      "          [0.2042],\n",
      "          ...,\n",
      "          [0.7997],\n",
      "          [0.1152],\n",
      "          [0.6186]],\n",
      "\n",
      "         ...,\n",
      "\n",
      "         [[0.9107],\n",
      "          [0.7045],\n",
      "          [0.9788],\n",
      "          ...,\n",
      "          [0.4865],\n",
      "          [0.7667],\n",
      "          [0.6143]],\n",
      "\n",
      "         [[0.3362],\n",
      "          [0.8065],\n",
      "          [0.9489],\n",
      "          ...,\n",
      "          [0.4016],\n",
      "          [0.7741],\n",
      "          [0.2940]],\n",
      "\n",
      "         [[0.3019],\n",
      "          [0.0304],\n",
      "          [0.3825],\n",
      "          ...,\n",
      "          [0.6217],\n",
      "          [0.2140],\n",
      "          [0.4040]]],\n",
      "\n",
      "\n",
      "        [[[0.8468],\n",
      "          [0.5219],\n",
      "          [0.6235],\n",
      "          ...,\n",
      "          [0.0654],\n",
      "          [0.9668],\n",
      "          [0.0895]],\n",
      "\n",
      "         [[0.8119],\n",
      "          [0.8621],\n",
      "          [0.5071],\n",
      "          ...,\n",
      "          [0.0556],\n",
      "          [0.5689],\n",
      "          [0.9977]],\n",
      "\n",
      "         [[0.4927],\n",
      "          [0.8083],\n",
      "          [0.7689],\n",
      "          ...,\n",
      "          [0.5880],\n",
      "          [0.8212],\n",
      "          [0.8383]],\n",
      "\n",
      "         ...,\n",
      "\n",
      "         [[0.0627],\n",
      "          [0.3816],\n",
      "          [0.8209],\n",
      "          ...,\n",
      "          [0.0987],\n",
      "          [0.8176],\n",
      "          [0.3932]],\n",
      "\n",
      "         [[0.9125],\n",
      "          [0.1979],\n",
      "          [0.3640],\n",
      "          ...,\n",
      "          [0.5621],\n",
      "          [0.4716],\n",
      "          [0.5283]],\n",
      "\n",
      "         [[0.2294],\n",
      "          [0.9390],\n",
      "          [0.3306],\n",
      "          ...,\n",
      "          [0.4917],\n",
      "          [0.1469],\n",
      "          [0.3527]]]])\n"
     ]
    }
   ],
   "source": [
    "a = torch.rand(4, 1, 28, 28)\n",
    "print(a.shape)\n",
    "\n",
    "# numel() 元素个数保存一致\n",
    "# 将后面的维度作为一维，忽略上下左右信息和通道信息，适合全连接层\n",
    "# 4, 1 * 28 * 28 [4 * 784]\n",
    "print(a.view(4, 28 * 28))\n",
    "\n",
    "print(a.view(4, 28 * 28).shape)\n",
    "\n",
    "# 合并前3个通道\n",
    "# 把所有的行放在一个维度\n",
    "# 4 * 1 * 28, 28 \n",
    "print(a.view(4 * 28, 28).shape)\n",
    "\n",
    "print(a.view(4 * 1, 28, 28).shape)\n",
    "\n",
    "b = a.view(4, 784)\n",
    "# 这是错误的方式，数据的存储维度顺序非常重要\n",
    "print(b.view(4, 28, 28, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a7b89cf",
   "metadata": {},
   "source": [
    "### Squeeze / unsqueeze 挤压/增加维度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "d40c1115",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 1, 28, 28])\n",
      "torch.Size([1, 4, 1, 28, 28])\n",
      "torch.Size([4, 1, 28, 28, 1])\n",
      "torch.Size([4, 1, 28, 28, 1])\n",
      "torch.Size([4, 1, 1, 28, 28])\n",
      "torch.Size([1, 4, 1, 28, 28])\n"
     ]
    }
   ],
   "source": [
    "# 增加维度\n",
    "\n",
    "print(a.shape)\n",
    "\n",
    "# 从0维度的索引前增加一个维度\n",
    "print(a.unsqueeze(0).shape)\n",
    "\n",
    "# 在最后的维度索引位置增加一个维度\n",
    "print(a.unsqueeze(-1).shape)\n",
    "\n",
    "print(a.unsqueeze(4).shape)\n",
    "\n",
    "print(a.unsqueeze(-4).shape)\n",
    "\n",
    "print(a.unsqueeze(-5).shape)\n",
    "\n",
    "# print(a.unsqueeze(5).shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "de1f3f3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1.2000],\n",
      "        [2.3000]])\n",
      "tensor([[1.2000, 2.3000]])\n"
     ]
    }
   ],
   "source": [
    "a = torch.tensor([1.2, 2.3])\n",
    "\n",
    "print(a.unsqueeze(-1))\n",
    "\n",
    "print(a.unsqueeze(0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "6dc7804b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([0.0913, 0.4005, 0.5267, 0.9623, 0.6328, 0.7209, 0.5081, 0.7640, 0.5649,\n",
      "        0.5350, 0.8185, 0.9119, 0.4940, 0.4863, 0.4129, 0.0779, 0.9812, 0.3756,\n",
      "        0.2126, 0.3859, 0.2279, 0.4235, 0.2485, 0.3647, 0.7250, 0.3192, 0.2607,\n",
      "        0.7474, 0.9941, 0.2538, 0.7096, 0.6417])\n",
      "torch.Size([1, 32, 1, 1])\n"
     ]
    }
   ],
   "source": [
    "# 处理图片的例子\n",
    "# bias相当于给每个channel上的所有像素增加一个偏置项\n",
    "b = torch.rand(32)\n",
    "print(b)\n",
    "\n",
    "f = torch.rand(4, 32, 14, 14)\n",
    "\n",
    "b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)\n",
    "\n",
    "print(b.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "7e2fa409",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 32, 1, 1])\n",
      "torch.Size([32])\n",
      "torch.Size([32, 1, 1])\n",
      "torch.Size([1, 32, 1])\n",
      "torch.Size([1, 32, 1, 1])\n",
      "torch.Size([32, 1, 1])\n"
     ]
    }
   ],
   "source": [
    "# 挤压\n",
    "\n",
    "print(b.shape)\n",
    "\n",
    "# 挤压成一维\n",
    "print(b.squeeze().shape)\n",
    "# 挤压掉0维\n",
    "print(b.squeeze(0).shape)\n",
    "\n",
    "print(b.squeeze(-1).shape)\n",
    "\n",
    "# 维度不是1维，则不能被挤压，返回原值\n",
    "print(b.squeeze(1).shape)\n",
    "\n",
    "print(b.squeeze(-4).shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2451afdf",
   "metadata": {},
   "source": [
    "### Expand / repeat 维度扩展"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "1d78a929",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 32, 1, 1])\n",
      "torch.Size([4, 32, 14, 14])\n",
      "torch.Size([1, 32, 1, 1])\n",
      "torch.Size([1, 32, 1, -4])\n"
     ]
    }
   ],
   "source": [
    "# expand\n",
    "# 优先使用 expand\n",
    "a = torch.rand(4, 32, 14, 14)\n",
    "\n",
    "print(b.shape)\n",
    "\n",
    "print(b.expand(4, 32, 14, 14).shape)\n",
    "\n",
    "# -1 表示维度保持不变\n",
    "print(b.expand(-1, 32, -1, -1).shape)\n",
    "\n",
    "# \n",
    "# 这个bug在最新版已经被Facebook修复了\n",
    "print(b.expand(-1, 32, 1, -4).shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "b722a26e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 32, 1, 1])\n",
      "torch.Size([4, 1024, 1, 1])\n",
      "torch.Size([4, 32, 1, 1])\n",
      "torch.Size([4, 32, 32, 32])\n"
     ]
    }
   ],
   "source": [
    "# repeat\n",
    "# repeat为复制数据，更加占用内存\n",
    "\n",
    "print(b.shape)\n",
    "\n",
    "# repeat参数为需要copy的次数\n",
    "print(b.repeat(4, 32, 1, 1).shape)\n",
    "\n",
    "print(b.repeat(4, 1, 1, 1).shape)\n",
    "\n",
    "print(b.repeat(4, 1, 32, 32).shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "897d03d5",
   "metadata": {},
   "source": [
    "### Transpose / t / permute 交换/转置/重排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "3ff9c825",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([3, 4])\n",
      "torch.Size([4, 3])\n"
     ]
    }
   ],
   "source": [
    "# 转置 t()\n",
    "a = torch.randn(3, 4)\n",
    "print(a.shape)\n",
    "\n",
    "print(a.t().shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "13cb0937",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 3, 32, 32])\n",
      "torch.Size([4, 3, 32, 32])\n",
      "torch.Size([4, 3, 32, 32])\n",
      "tensor(False)\n",
      "tensor(True)\n"
     ]
    }
   ],
   "source": [
    "# 维度交换 transpose\n",
    "\n",
    "a = torch.rand(4, 3, 32, 32)\n",
    "print(a.shape)\n",
    "\n",
    "# 数据的维度顺序必须和存储顺序一致\n",
    "# 错误：\n",
    "# 需要使用contiguous()函数把数据变成一维，再交换\n",
    "# a1 = a.transpose(1, 3).view(4, 3*32*32).view(4, 3, 32, 32)\n",
    "\n",
    "# [b c h w]  [b w h c] [b c w h]\n",
    "# 错误：view操作丢失了维度信息\n",
    "a1 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 3, 32, 32)\n",
    "\n",
    "# 正确\n",
    "# [b c h w] [b w h c] => [b w h c] [b c h w]\n",
    "# 经过若干操作后，数据还原\n",
    "a2 = a.transpose(1, 3).contiguous().view(4, 3*32*32).view(4, 32, 32, 3).transpose(1, 3)\n",
    "\n",
    "print(a1.shape)\n",
    "print(a2.shape)\n",
    "\n",
    "print(torch.all(torch.eq(a, a1)))\n",
    "print(torch.all(torch.eq(a, a2)))\n",
    "\n",
    "# view会导致维度顺序关系变模糊，所以需要认为跟踪"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "735471a9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([4, 28, 28, 3])\n",
      "torch.Size([4, 32, 28, 3])\n",
      "torch.Size([4, 28, 32, 3])\n",
      "torch.Size([4, 28, 32, 3])\n"
     ]
    }
   ],
   "source": [
    "# permute\n",
    "\n",
    "a = torch.rand(4, 3, 28, 28)\n",
    "\n",
    "print(a.transpose(1, 3).shape)\n",
    "\n",
    "b = torch.rand(4, 3, 28, 32)\n",
    "print(b.transpose(1, 3).shape)\n",
    "\n",
    "print(b.transpose(1, 3).transpose(1, 2).shape)\n",
    "\n",
    "print(b.permute(0, 2, 3, 1).shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "453f47c8",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
